import urllib.parse
import json
from impl._baidu_misc import _get_valid_json, _parse_baidu_json
from impl.web_driver import get_default_web_driver
import time
from datetime import datetime
debug = True


def _no_image_interceptor(request):
    if 'it/u=' in request.path:
        request.abort()


def _check_has_valid_response(request):
    response = request.response

    if response is None:
        return False

    if response.status_code < 200 or response.status_code >= 400:
        return False

    if response.body is None or len(response.body) == 0:
        return False

    return True


def _parse_web_page(request, results):
    if not _check_has_valid_response(request):
        return
    prefetch_content = request.response.body
    pattern = b"app.setData('imgData', "
    index = prefetch_content.find(pattern)
    assert index != -1
    index += len(pattern)
    end_pattern = b');'
    end_index = prefetch_content.find(b'app.setData(', index)
    if end_index != -1:
        end_index = prefetch_content.rfind(end_pattern, index, end_index)
    else:
        end_index = prefetch_content.find(end_pattern, index)
    assert end_index != -1
    prefetch_content = prefetch_content[index: end_index]
    try:
        results.extend(_parse_baidu_json(json.loads(_get_valid_json(prefetch_content), strict=False)))
    except json.JSONDecodeError:
        if debug:
            with open(f'{datetime.now().strftime("%Y.%m.%d-%H.%M.%S-%f")}.html', 'wb') as f:
                f.write(request.response.body)
        raise


def _parse_XHR(request, results):
    if not _check_has_valid_response(request):
        return
    try:
        results.extend(_parse_baidu_json(json.loads(_get_valid_json(request.response.body), strict=False)))
    except json.JSONDecodeError:
        if debug:
            with open(f'{datetime.now().strftime("%Y.%m.%d-%H.%M.%S-%f")}.json', 'wb') as f:
                f.write(request.response.body)
        raise


def _parse_requests(requests):
    results = []
    for request in requests:
        if request.url.startswith('https://image.baidu.com/search/index?tn=baiduimage'):
            _parse_web_page(request, results)
        elif request.url.startswith('https://image.baidu.com/search/acjson?tn=resultjson_com'):
            _parse_XHR(request, results)
    return results


class BaiduImageWebDriverCrawler:
    def __init__(self, search_word, min_wait_time, retry_times, headless):
        web_driver = get_default_web_driver(headless=headless)
        web_driver.request_interceptor = _no_image_interceptor
        self.web_driver = web_driver
        self.min_wait_time = min_wait_time
        self.retry_times = retry_times
        self.url_search_name = urllib.parse.quote(search_word)
        self.document_height = 0

    def __enter__(self):
        self.web_driver.__enter__()
        self.web_driver.get(f'https://image.baidu.com/search/index?tn=baiduimage&word={self.url_search_name}')
        self.timer_begin = time.perf_counter()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.web_driver.__exit__(exc_type, exc_val, exc_tb)

    def _raw_get_next(self):
        elapsed_time = time.perf_counter()
        if elapsed_time - self.timer_begin < self.min_wait_time:
            time.sleep(self.min_wait_time - (elapsed_time - self.timer_begin))
        results = _parse_requests(self.web_driver.requests)
        del self.web_driver.requests
        document_height = self.web_driver.execute_script('''
            window.scrollTo(0, document.body.scrollHeight);
            return document.body.scrollHeight;
        ''')
        make_difference = document_height != self.document_height
        self.document_height = document_height
        self.timer_begin = time.perf_counter()
        if len(results) == 0:
            results = None
        return make_difference, results

    def get_next(self):
        i = 0
        while True:
            make_difference, results = self._raw_get_next()

            if results is not None:
                return results
            if make_difference:
                i = 0
            else:
                i += 1
            if i >= self.retry_times:
                break

        return None
